package com.example.netcapture.utils;

import com.example.netcapture.entity.PacketInfo;
import lombok.extern.slf4j.Slf4j;
import org.pcap4j.packet.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class PackageParser {

    /**
     * 解析数据包的主方法
     */
    public static PacketInfo parsePacket(Packet packet, String networkInterface) {
        try {
            PacketInfo info = new PacketInfo();
            info.setCaptureTime(LocalDateTime.now());
            info.setPacketLength(packet.length());
            info.setNetworkInterface(networkInterface);

            // 以太网帧
            EthernetPacket ethernetPacket = packet.get(EthernetPacket.class);
            if (ethernetPacket == null) {
                return null;
            }

            // IPV4数据包，提取IP
            IpV4Packet ipPacket = ethernetPacket.getPayload().get(IpV4Packet.class);
            if (ipPacket != null) {
                info.setSourceIp(ipPacket.getHeader().getSrcAddr().getHostAddress());
                info.setDestinationIp(ipPacket.getHeader().getDstAddr().getHostAddress());

                // 传输层：提取端口
                TcpPacket tcpPacket = ipPacket.getPayload().get(TcpPacket.class);
                if (tcpPacket != null) {
                    info.setProtocol("TCP");
                    info.setSourcePort(tcpPacket.getHeader().getSrcPort().valueAsInt());
                    info.setDestinationPort(tcpPacket.getHeader().getDstPort().valueAsInt());
                    // 确保正确获取序列号（使用无符号整数处理）
                    long seqNumber = tcpPacket.getHeader().getSequenceNumber() & 0xFFFFFFFFL;
                    info.setTcpSeqNumber(seqNumber);
                    info.setTcpAckNumber((long) tcpPacket.getHeader().getAcknowledgmentNumber());
                    info.setTcpFlags(tcpPacket.getHeader().toString());

                    // 生成会话ID（双向会话使用相同的ID）
                    String sessionId = generateSessionId(info.getSourceIp(), info.getSourcePort(),
                            info.getDestinationIp(), info.getDestinationPort());
                    info.setSessionId(sessionId);

                    // 获取TCP负载
                    byte[] payload = tcpPacket.getPayload() != null ?
                            tcpPacket.getPayload().getRawData() : null;

                    if (payload != null && payload.length > 0) {
                        // 改进：增强的负载处理，支持HTTP响应和文件类型检测
                        processPayloadEnhanced(info, payload, sessionId);

                        // 添加到会话管理
                        TcpSessionManager.TcpSession session =
                                TcpSessionManager.getOrCreateSession(sessionId);
                        session.addPacket(info);
                    }

                } else {
                    // UDP处理保持不变
                    UdpPacket udpPacket = ipPacket.getPayload().get(UdpPacket.class);
                    if (udpPacket != null) {
                        info.setProtocol("UDP");
                        info.setSourcePort(udpPacket.getHeader().getSrcPort().valueAsInt());
                        info.setDestinationPort(udpPacket.getHeader().getDstPort().valueAsInt());
                        String sessionId = generateSessionId(info.getSourceIp(), info.getSourcePort(),
                                info.getDestinationIp(), info.getDestinationPort());
                        info.setSessionId(sessionId);

                        byte[] payload = udpPacket.getPayload() != null ?
                                udpPacket.getPayload().getRawData() : null;
                        if (payload != null && payload.length > 0) {
                            // 改进：增强的负载处理，支持HTTP响应和文件类型检测
                            processPayloadEnhanced(info, payload, sessionId);
                        }
                    } else {
                        info.setProtocol("OTHER");
                    }
                }
            } else {
                info.setProtocol("UNKNOWN");
            }

            return info;
        } catch (Exception e) {
            log.error("解析数据包失败", e);
            return null;
        }
    }

    // 生成双向会话ID（确保A->B和B->A使用相同ID）
    private static String generateSessionId(String srcIp, int srcPort, String dstIp, int dstPort) {
        // 按IP和端口排序，确保双向流量使用相同会话ID
        if (srcIp.compareTo(dstIp) > 0 ||
                (srcIp.equals(dstIp) && srcPort > dstPort)) {
            return dstIp + ":" + dstPort + "-" + srcIp + ":" + srcPort;
        }
        return srcIp + ":" + srcPort + "-" + dstIp + ":" + dstPort;
    }

    /**
     * 改进的payload处理方法 - 支持HTTP响应解析和文件类型检测
     */
    public static void processPayloadEnhanced(PacketInfo info, byte[] payload, String sessionId) {
        int maxLength = Math.min(payload.length, 10240);
        byte[] truncatedPayload = new byte[maxLength];
        System.arraycopy(payload, 0, truncatedPayload, 0, maxLength);

        String hexPayload = bytesToHex(truncatedPayload);
        info.setHexData(hexPayload);

        // 第一步：检查是否为HTTP响应
        HttpParseResult httpResult = parseHttpResponse(payload);
        if (httpResult != null && httpResult.isHttpResponse) {
            // 处理HTTP响应
            processHttpResponsePayload(info, httpResult, sessionId);
            return;
        }

        // 第二步：如果不是HTTP响应，检查是否为已知的二进制文件类型
        FileTypeDetectionResult fileType = detectFileType(truncatedPayload);
        if (fileType != null && !fileType.isText) {
            // 处理二进制文件数据
            processBinaryFileData(info, truncatedPayload, fileType, sessionId);
            return;
        }

        // 第三步：检查是否为可打印文本
        if (isPrintableText(truncatedPayload)) {
            String payloadStr = tryMultipleEncodings(truncatedPayload);
            info.setPayload(payloadStr);
            info.setDataType("TEXT");
        } else {
            // 对于未知的二进制数据
            String protocolHint = detectProtocol(truncatedPayload);
            info.setPayload("[Binary Data - " + protocolHint + ", Size: " + truncatedPayload.length + " bytes]");
            info.setDataType("BINARY");
        }
    }

    /**
     * HTTP解析结果类
     */
    private static class HttpParseResult {
        public boolean isHttpResponse;
        public Map<String, String> headers;
        public byte[] body;
        public int statusCode;
        public String contentType;
        public String contentDisposition;
        public String filename;
        public int contentLength;

        public HttpParseResult() {
            this.headers = new HashMap<>();
            this.body = new byte[0];
        }
    }

    /**
     * 解析HTTP响应
     */
    private static HttpParseResult parseHttpResponse(byte[] data) {
        if (data == null || data.length < 12) {
            return null;
        }

        try {
            String dataStr = new String(data, java.nio.charset.StandardCharsets.ISO_8859_1);

            // 检查是否为HTTP响应
            if (!dataStr.startsWith("HTTP/")) {
                return null;
            }

            HttpParseResult result = new HttpParseResult();
            result.isHttpResponse = true;

            // 解析状态行
            int firstLineEnd = dataStr.indexOf("\r\n");
            if (firstLineEnd == -1) return null;

            String statusLine = dataStr.substring(0, firstLineEnd);
            String[] statusParts = statusLine.split(" ");
            if (statusParts.length >= 2) {
                try {
                    result.statusCode = Integer.parseInt(statusParts[1]);
                } catch (NumberFormatException e) {
                    result.statusCode = 0;
                }
            }

            // 解析头部
            int headerEnd = dataStr.indexOf("\r\n\r\n");
            if (headerEnd == -1) {
                // 如果没有找到完整的头部结束标记，可能是不完整的HTTP响应
                return result;
            }

            String headersSection = dataStr.substring(firstLineEnd + 2, headerEnd);
            String[] headerLines = headersSection.split("\r\n");

            for (String line : headerLines) {
                int colonIndex = line.indexOf(":");
                if (colonIndex > 0) {
                    String key = line.substring(0, colonIndex).trim();
                    String value = line.substring(colonIndex + 1).trim();
                    result.headers.put(key, value);

                    // 提取重要头部信息
                    if (key.equalsIgnoreCase("Content-Type")) {
                        result.contentType = value;
                    } else if (key.equalsIgnoreCase("Content-Disposition")) {
                        result.contentDisposition = value;
                        // 从Content-Disposition中提取文件名
                        if (value.contains("filename=")) {
                            int start = value.indexOf("filename=") + 9;
                            int end = value.length();
                            if (value.indexOf(";", start) != -1) {
                                end = value.indexOf(";", start);
                            }
                            result.filename = value.substring(start, end).replace("\"", "").trim();
                        }
                    } else if (key.equalsIgnoreCase("Content-Length")) {
                        try {
                            result.contentLength = Integer.parseInt(value);
                        } catch (NumberFormatException e) {
                            result.contentLength = 0;
                        }
                    }
                }
            }

            // 提取响应体
            int bodyStart = headerEnd + 4; // \r\n\r\n 的长度
            if (bodyStart < data.length) {
                result.body = new byte[data.length - bodyStart];
                System.arraycopy(data, bodyStart, result.body, 0, result.body.length);
            }

            return result;

        } catch (Exception e) {
            log.debug("解析HTTP响应失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 处理HTTP响应payload
     */
    private static void processHttpResponsePayload(PacketInfo info, HttpParseResult httpResult, String sessionId) {
        info.setDataType("HTTP_RESPONSE");
        info.setProtocol("HTTP");

        // 检查是否为文件下载响应或二进制内容
        boolean isFileDownload = httpResult.statusCode == 200 &&
                (httpResult.contentDisposition != null &&
                        httpResult.contentDisposition.contains("attachment")) ||
                (httpResult.contentType != null &&
                        (httpResult.contentType.startsWith("image/") ||
                                httpResult.contentType.startsWith("application/") ||
                                httpResult.contentType.contains("octet-stream")));

        if (isFileDownload && httpResult.body.length > 0) {
            // 处理文件下载
            processHttpFileDownload(info, httpResult, sessionId);
        } else if (httpResult.body.length > 0) {
            // 处理普通HTTP响应体 - 传入sessionId
            processHttpResponseBody(info, httpResult, sessionId);
        } else {
            // 只有头部没有内容
            info.setPayload("[HTTP Response " + httpResult.statusCode + " - Headers Only]");
        }
    }

    /**
     * 处理HTTP文件下载
     */
    private static void processHttpFileDownload(PacketInfo info, HttpParseResult httpResult, String sessionId) {
        info.setDataType("FILE_DOWNLOAD");

        // 从Content-Type推断文件类型
        String fileType = inferFileTypeFromContentType(httpResult.contentType);
        if (fileType == null) {
            fileType = "UNKNOWN";
        }

        info.setFileType(fileType);
        info.setMimeType(httpResult.contentType);

        // 设置payload显示信息
        String fileInfo = String.format("[HTTP File Download: %s, Size: %d bytes, Type: %s]",
                httpResult.filename != null ? httpResult.filename : "unknown",
                httpResult.body.length,
                fileType);
        info.setPayload(fileInfo);

        // 记录文件数据到会话管理器
        TcpSessionManager.TcpSession session = TcpSessionManager.getOrCreateSession(sessionId);
        session.addFileData(httpResult.body, fileType, info.getTcpSeqNumber());

        log.debug("检测到HTTP文件下载: {} -> {}, 文件: {}, 类型: {}, 大小: {} bytes",
                info.getSourceIp(), info.getDestinationIp(),
                httpResult.filename, fileType, httpResult.body.length);
    }

    /**
     * 处理普通HTTP响应体
     */
    private static void processHttpResponseBody(PacketInfo info, HttpParseResult httpResult, String sessionId) {
        // 检查是否为二进制内容类型
        boolean isBinaryContent = httpResult.contentType != null &&
                (httpResult.contentType.startsWith("image/") ||
                        httpResult.contentType.startsWith("application/") ||
                        httpResult.contentType.contains("octet-stream"));

        // 如果是PNG图片或其他二进制内容
        if (isBinaryContent && httpResult.body.length > 0) {
            // 检测具体的文件类型
            FileTypeDetectionResult fileType = detectFileType(httpResult.body);
            if (fileType != null) {
                // 处理为二进制文件
                processBinaryFileData(info, httpResult.body, fileType, sessionId);
                return;
            } else if (httpResult.contentType.contains("png")) {
                // 明确是PNG类型但魔数检测失败，手动设置
                FileTypeDetectionResult pngType = new FileTypeDetectionResult(
                        "PNG", "png", "image/png", false);
                processBinaryFileData(info, httpResult.body, pngType, sessionId);
                return;
            }
        }

        // 文本内容处理（原有逻辑）
        if (isPrintableText(httpResult.body)) {
            String bodyText = tryMultipleEncodings(httpResult.body);
            int displayLength = Math.min(bodyText.length(), 1000);
            String displayText = bodyText.substring(0, displayLength);
            if (bodyText.length() > displayLength) {
                displayText += "... [truncated]";
            }
            //info.setPayload("[HTTP Response Body]\n" + displayText);
            info.setPayload(displayText);
        } else {
            // 即使不是可打印文本，也尝试保存为二进制数据
            FileTypeDetectionResult detectedType = detectFileType(httpResult.body);
            if (detectedType != null) {
                processBinaryFileData(info, httpResult.body, detectedType, sessionId);
            } else {
                info.setPayload("[HTTP Response Body - Binary Data, Size: " + httpResult.body.length + " bytes]");
            }
        }
    }

    /**
     * 从Content-Type推断文件类型
     */
    private static String inferFileTypeFromContentType(String contentType) {
        if (contentType == null) return null;

        if (contentType.contains("text/plain")) return "TXT";
        if (contentType.contains("text/html")) return "HTML";
        if (contentType.contains("application/json")) return "JSON";
        if (contentType.contains("application/xml")) return "XML";
        if (contentType.contains("image/jpeg")) return "JPEG";
        if (contentType.contains("image/png")) return "PNG";
        if (contentType.contains("application/pdf")) return "PDF";
        if (contentType.contains("application/zip")) return "ZIP";
        if (contentType.contains("application/octet-stream")) return "BINARY";

        return contentType.split(";")[0].replace("/", "_").toUpperCase();
    }

    /**
     * 文件类型检测结果类
     */
    private static class FileTypeDetectionResult {
        private final String fileType;
        private final String extension;
        private final String mimeType;
        private final boolean isText;

        public FileTypeDetectionResult(String fileType, String extension, String mimeType, boolean isText) {
            this.fileType = fileType;
            this.extension = extension;
            this.mimeType = mimeType;
            this.isText = isText;
        }

        // Getters
        public String getFileType() { return fileType; }
        public String getExtension() { return extension; }
        public String getMimeType() { return mimeType; }
        public boolean isText() { return isText; }
    }

    /**
     * 检测文件类型
     */
    private static FileTypeDetectionResult detectFileType(byte[] data) {
        if (data == null || data.length < 4) {
            return null;
        }

        // 检查常见的文件魔数
        if (startsWith(data, new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF})) {
            return new FileTypeDetectionResult("JPEG", "jpg", "image/jpeg", false);
        }
        if (startsWith(data, new byte[]{0x47, 0x49, 0x46, 0x38})) { // GIF8
            return new FileTypeDetectionResult("GIF", "gif", "image/gif", false);
        }
        if (startsWith(data, new byte[]{(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A})) { // PNG
            return new FileTypeDetectionResult("PNG", "png", "image/png", false);
        }
        if (startsWith(data, new byte[]{0x25, 0x50, 0x44, 0x46})) { // PDF
            return new FileTypeDetectionResult("PDF", "pdf", "application/pdf", false);
        }
        if (startsWith(data, new byte[]{0x50, 0x4B, 0x03, 0x04})) { // ZIP
            return new FileTypeDetectionResult("ZIP", "zip", "application/zip", false);
        }
        if (startsWith(data, new byte[]{0x52, 0x61, 0x72, 0x21})) { // RAR
            return new FileTypeDetectionResult("RAR", "rar", "application/x-rar-compressed", false);
        }
        if (startsWith(data, new byte[]{0x1F, (byte) 0x8B})) { // GZIP
            return new FileTypeDetectionResult("GZIP", "gz", "application/gzip", false);
        }
        if (startsWith(data, new byte[]{0x42, 0x4D})) { // BMP
            return new FileTypeDetectionResult("BMP", "bmp", "image/bmp", false);
        }
        if (startsWith(data, new byte[]{0x49, 0x49, 0x2A, 0x00}) || // TIFF little-endian
                startsWith(data, new byte[]{0x4D, 0x4D, 0x00, 0x2A})) { // TIFF big-endian
            return new FileTypeDetectionResult("TIFF", "tiff", "image/tiff", false);
        }

        return null;
    }

    /**
     * 处理二进制文件数据
     */
    private static void processBinaryFileData(PacketInfo info, byte[] data,
                                              FileTypeDetectionResult fileType, String sessionId) {
        info.setDataType("FILE");
        info.setFileType(fileType.getFileType());
        info.setMimeType(fileType.getMimeType());

        // 设置payload显示信息
        String fileInfo = String.format("[File: %s, Size: %d bytes, Type: %s]",
                fileType.getFileType(), data.length, fileType.getMimeType());
        info.setPayload(fileInfo);

        // 记录文件数据到会话管理器（使用序列号）
        TcpSessionManager.TcpSession session = TcpSessionManager.getOrCreateSession(sessionId);
        session.addFileData(data, fileType.getFileType(), info.getTcpSeqNumber());

        log.debug("检测到文件传输: {} -> {}, 类型: {}, 大小: {} bytes",
                info.getSourceIp(), info.getDestinationIp(),
                fileType.getFileType(), data.length);
    }

    /**
     * 字节数组起始匹配检查
     */
    private static boolean startsWith(byte[] source, byte[] target) {
        return startsWith(source, 0, target);
    }

    private static boolean startsWith(byte[] source, int offset, byte[] target) {
        if (source.length - offset < target.length) {
            return false;
        }
        for (int i = 0; i < target.length; i++) {
            if (source[offset + i] != target[i]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 改进的协议检测，增加更多文件传输协议
     */
    private static String detectProtocol(byte[] data) {
        if (data.length >= 3) {
            // 检查HTTP
            String start = new String(data, 0, Math.min(10, data.length));
            if (start.startsWith("GET") || start.startsWith("POST") ||
                    start.startsWith("PUT") || start.startsWith("HTTP") ||
                    start.startsWith("HEAD") || start.startsWith("DELETE")) {
                return "HTTP";
            }

            // 检查FTP
            if (start.startsWith("220") || start.startsWith("USER") ||
                    start.startsWith("PASS") || start.startsWith("RETR")) {
                return "FTP";
            }

            // 检查TLS/SSL
            if (data[0] == 0x16 || data[0] == 0x17) { // TLS记录类型
                return "TLS";
            }

            // 检查SMTP
            if (start.startsWith("220") && start.contains("SMTP") ||
                    start.startsWith("HELO") || start.startsWith("MAIL FROM")) {
                return "SMTP";
            }
        }

        // 检查是否为常见的文件传输数据
        FileTypeDetectionResult fileType = detectFileType(data);
        if (fileType != null) {
            return "FILE_TRANSFER_" + fileType.getFileType();
        }

        return "Unknown";
    }

    /**
     * 检测是否为可打印文本
     */
    public static boolean isPrintableText(byte[] data) {
        if (data == null || data.length == 0) return false;

        int printableCount = 0;
        for (byte b : data) {
            // ASCII可打印字符范围：32-126，以及制表符、换行符等
            if ((b >= 32 && b <= 126) || b == 9 || b == 10 || b == 13) {
                printableCount++;
            }
        }

        // 如果80%以上的字节都是可打印字符，认为是文本
        return (printableCount * 100 / data.length) >= 80;
    }

    /**
     * 尝试多种编码解析
     */
    public static String tryMultipleEncodings(byte[] data) {
        String[] encodings = {"UTF-8", "ISO-8859-1", "Windows-1252", "GBK", "GB2312"};

        for (String encoding : encodings) {
            try {
                String result = new String(data, encoding);
                // 检查结果是否包含过多乱码字符
                if (!containsTooManyGarbledChars(result)) {
                    return result;
                }
            } catch (Exception e) {
                // 忽略编码异常，尝试下一个
            }
        }

        // 如果所有编码都失败，返回ISO-8859-1（最兼容）
        return new String(data, java.nio.charset.StandardCharsets.ISO_8859_1);
    }

    /**
     * 检查字符串是否包含过多乱码字符
     */
    public static boolean containsTooManyGarbledChars(String str) {
        if (str == null || str.isEmpty()) return false;

        int garbledCount = 0;
        for (char c : str.toCharArray()) {
            // 认为是乱码的字符：替换字符、未定义字符等
            if (c == '?' || c == '\ufffd' || Character.isISOControl(c) && !isCommonControlChar(c)) {
                garbledCount++;
            }
        }

        return (garbledCount * 100 / str.length()) > 20; // 超过20%认为是过多乱码
    }

    /**
     * 常见控制字符检查
     */
    public static boolean isCommonControlChar(char c) {
        return c == '\n' || c == '\r' || c == '\t';
    }

    /**
     * 字节数组转16进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex).append(' ');

            // 每16字节换行，便于阅读
            if (hexString.length() % 48 == 0) {
                hexString.append('\n');
            }
        }
        return hexString.toString().trim();
    }

    /**
     * 获取payload预览
     */
    public String getPayloadPreview(byte[] data) {
        if (data == null || data.length == 0) return "";

        int previewLength = Math.min(data.length, 50);
        byte[] preview = new byte[previewLength];
        System.arraycopy(data, 0, preview, 0, previewLength);

        if (isPrintableText(preview)) {
            String text = new String(preview, java.nio.charset.StandardCharsets.ISO_8859_1);
            // 替换控制字符为可见符号
            return text.replaceAll("\\p{Cntrl}", ".");
        } else {
            return bytesToHex(preview);
        }
    }
}
